package io.renren.common.utils.PayUtils.WxPay.weixin;

import io.renren.common.utils.PayUtils.WxPay.weixin.util.ConstantUtil;
import io.renren.common.utils.PayUtils.WxPay.weixin.util.MD5Util;
import io.renren.common.utils.PayUtils.WxPay.weixin.util.XMLUtil;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Created by sk1 on 2016/8/16.
 */
public class SignValidateUtil {

    /**
     * 构造函数
     */
    public SignValidateUtil() {
    }


    public static boolean checkIsSignValidFromResponseString(String responseString) {

        try {
            SortedMap<String, Object> map = doXMLParse(responseString);
            //Map<String, Object> map = XMLParser.getMapFromXML(responseString);
            System.out.println(map.toString());
            String signFromAPIResponse = map.get("sign").toString();
            if ("".equals(signFromAPIResponse) || signFromAPIResponse == null) {
                System.out.println("API返回的数据签名数据不存在，有可能被第三方篡改!!!");
                return false;
            }
            System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
            //清掉返回数据对象里面的Sign数据（不能把这个数据也加进去进行签名），然后用签名算法进行签名
            map.put("sign", "");
            //将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
            String signForAPIResponse = createSign("UTF-8", map);
            if (!signForAPIResponse.equals(signFromAPIResponse)) {
                //签名验不过，表示这个API返回的数据有可能已经被篡改了
                System.out.println("API返回的数据签名验证不通过，有可能被第三方篡改!!!");
                return false;
            }
            System.out.println("恭喜，API返回的数据签名验证通过!!!");
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 签名工具
     *
     * @param characterEncoding 编码格式 UTF-8
     * @param parameters        请求参数
     * @return
     * @author wangkai
     * @date 2014-12-5下午2:29:34
     * @Description：sign签名
     */
    public static String createSign(String characterEncoding,
                                    Map<String, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
            String key = (String) entry.getKey();
            Object value = entry.getValue();//去掉带sign的项
            if (null != value && !"".equals(value) && !"sign".equals(key)
                    && !"key".equals(key)) {
                sb.append(key + "=" + value + "&");
            }
        }
        sb.append("key=" + ConstantUtil.WX_API_KEY); 
        //注意sign转为大写
        System.out.println(sb.toString());
        return MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    }

    /**
     * 解析xml,返回第一级元素键值对。
     * 如果第一级元素有子节点，
     * 则此节点的值是子节点的xml数据。
     *
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static SortedMap<String, Object> doXMLParse(String strxml)
            throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if (null == strxml || "".equals(strxml)) {
            return null;
        }
        SortedMap<String, Object> map = new TreeMap<String, Object>();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String key = e.getName();
            String value = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                value = e.getTextNormalize();
            } else {
                value = XMLUtil.getChildrenText(children);
            }
            map.put(key, value);
        }
        // 关闭流
        in.close();
        return map;
    }

    public static void main(String[] args) {
        String xml = "<xml>\n" +
                "    <appid>wx2ec2d66a48d49ca4</appid>\n" +
                "    <bank_type>CMBC_DEBIT</bank_type>\n" +
                "    <cash_fee>1</cash_fee>\n" +
                "    <fee_type>CNY</fee_type>\n" +
                "    <is_subscribe>N</is_subscribe>\n" +
                "    <mch_id>1354738002</mch_id>\n" +
                "    <nonce_str>f18224a1adfb7b3dbff668c9b655a35a</nonce_str>\n" +
                "    <openid>okNNjwpe0xd6VhkTcwsd8rld7nN8</openid>\n" +
                "    <out_trade_no>1471250593403691</out_trade_no>\n" +
                "    <result_code>SUCCESS</result_code>\n" +
                "    <return_code>SUCCESS</return_code>\n" +
                "    <sign>5A7CE4EF196F88D9E58AE6CEFF1633D3</sign>\n" +
                "    <time_end>20160816104234</time_end>\n" +
                "    <total_fee>1</total_fee>\n" +
                "    <trade_type>APP</trade_type>\n" +
                "    <transaction_id>4009182001201608161459110506</transaction_id>\n" +
                "</xml>";

        Boolean b = checkIsSignValidFromResponseString(xml);
        if (b) {
            System.out.println("验证成功");

        } else {
            System.out.println("验证失败");
        }


    }
}
